import xlrd
import codecs


class SimilarityCalculation(object):
    def __init__(self):
        self.tongue_map = r'/home/ubuntu/data/web/app/data_file_api/tongue_mapping.txt'  # 舌照特征数字与汉字映射表
        self.inquiry_map_file = r'/home/ubuntu/data/web/app/data_file_api/inquiry_mapping_1119.txt'  # 标准问诊单映射文件
        self.inquiry_map = self._read_inquiry_mapping(self.inquiry_map_file)  # 标准问诊单映射字典
        self.disease_name = r"/home/ubuntu/data/web/app/data_file_api/常见疾病列表.xlsx"  # 疾病词表文件
        self.symptom_name = r"/home/ubuntu/data/web/app/data_file_api/symptom_list"  # 症状词表文件
        self.word_list = self._word_list()  # 综合词表
        self.dict_tongue = self._tongue_map()  # 获取舌照特征数字与汉字对应的字典,获取舌照的汉字特征列表

    @staticmethod
    def tongue_distance(vec_01, vec_02):
        """
        舌照差异度计算
        :param vec_01:舌照向量列表的字符串'6.33,0.67,2,2.67,3,0,0,1,0.33,1.33,2.67,3,5,0,1.33'
        :param vec_02:舌照向量列表的字符串'6.33,0.67,2,2.67,3,0,0,1,0.33,1.33,2.67,3,5,0,1.33'
        :return: 两个舌照向量之间的距离
        """
        distance = 0
        if vec_01 != "NULL" and vec_02 != "NULL":
            # 正常值处于中间，两端均为异常的舌照特征索引
            double_index = {0, 3, 4, 11, 12}
            # 将字符串向量转换成数字列表
            vec_01_list = [float(item) for item in vec_01.split(",")]
            vec_02_list = [float(item) for item in vec_02.split(",")]
            # for index in range(len(vec_01.split(","))):
            #     vec_01_list.append(float(vec_01.split(",")[index]))
            #     vec_02_list.append(float(vec_02.split(",")[index]))
            # 求两个向量之间的距离
            distance = 0
            for i, val1 in enumerate(vec_01_list):
                if i in double_index:
                    # 给两端为异常的特征加上权重，乘以2，以表达更大的差异
                    distance += 2 * abs(val1 - vec_02_list[i])
                else:
                    distance += abs(val1 - vec_02_list[i])
        return distance

    # 将舌照特征转换成汉字形式
    def tongue_vec_feature(self, vec):
        # 将传进来的向量列表字符串转化成汉字的特征
        # feature = ''
        if vec != 'NULL':
            # for i in range(len(vec.split(","))):
            #     # 对特征进行四舍五入取整
            #     key_num = str(i) + '-' + str(int(float(vec.split(",")[i]) + 0.5))
            #     feature += self.dict_tongue[key_num] + ','
            feature_list = [self.dict_tongue['{0}-{1}'.format(i, round(float(item)))]
                            for i, item in enumerate(vec.split(','))]
            feature = ','.join(feature_list)
        else:
            feature = 'NULL'
        return feature

    # 获取舌照特征数字与汉字对应的字典
    def _tongue_map(self):
        map_tongue = open(self.tongue_map, encoding='UTF-8-sig').readlines()
        dict_tongue = {}
        for line in map_tongue:
            slip = line.strip('\n').split('\t')
            value = slip[-1].replace("##", ":")
            dict_tongue[slip[0]] = value
        return dict_tongue

    # 获取舌照特征的对比字典
    def tongue_contrast(self, vec_01, vec_02):
        contrast_dict = {}
        if vec_01 != "NULL" and vec_02 != "NULL":
            contrast_dict = {"same_feature": [], "different_feature": [], "same_normal": []}
            feature_01 = self.tongue_vec_feature(vec_01).split(",")[:-1]
            feature_02 = self.tongue_vec_feature(vec_02).split(",")[:-1]
            # print(feature_01, feature_02)
            # 正常的舌照特征-数字
            normal_tongue_list = ['0-3', '1-0', '2-0', '3-3', '4-3', '5-0', '6-0', '7-0', '8-0',
                                  '9-0', '10-0', '10-1', '11-3', '12-3', '13-0', '14-0']
            # 正常的舌照特征-汉字
            normal_tongue_list_literal = []
            for item in normal_tongue_list:
                normal_tongue_list_literal.append(self.dict_tongue[item])
            # 生成两个向量的对比字典
            for index in range(len(feature_01)):
                if feature_01[index] == feature_02[index]:
                    if feature_02[index] in normal_tongue_list_literal:
                        contrast_dict["same_normal"].append([feature_01[index], feature_02[index]])
                    else:
                        contrast_dict["same_feature"].append([feature_01[index], feature_02[index]])
                else:
                    contrast_dict["different_feature"].append([feature_01[index], feature_02[index]])
        return contrast_dict

    # 计算问诊单相似度
    def inquiry_distance(self, source_inquiry_dic, target_inquiry_dic, weight_aas=2, weight_nad=-1):
        same_question_set = set(source_inquiry_dic.keys()).intersection(set(target_inquiry_dic.keys()))
        score = 0
        for same_question in same_question_set:
            if '正常' in source_inquiry_dic[same_question]:
                source_inquiry_dic[same_question].remove('正常')
            if '正常' in target_inquiry_dic[same_question]:
                target_inquiry_dic[same_question].remove('正常')
            temp_samp_option_set = source_inquiry_dic[same_question].intersection(target_inquiry_dic[same_question])
            score += weight_aas * len(temp_samp_option_set)
            score += weight_nad * (len(source_inquiry_dic[same_question]) + len(target_inquiry_dic[same_question])
                                   - 2 * len(temp_samp_option_set))
        return score

    # 获取问诊单对比展示结果
    def inquiry_display(self, source_inquiry_dic, source_remain_list, target_inquiry_dic, target_remain_list):
        same_question_set = set(source_inquiry_dic.keys()).intersection(set(target_inquiry_dic.keys()))
        abnormal_same_list = []
        normal_diff_list = []
        normal_same_list = []
        tres_remain_list = self._get_diff_terms(target_inquiry_dic, same_question_set)
        tres_remain_list.extend(target_remain_list)
        sres_remain_list = self._get_diff_terms(source_inquiry_dic, same_question_set)
        sres_remain_list.extend(source_remain_list)
        for same_question in same_question_set:
            temp_samp_option_set = source_inquiry_dic[same_question].intersection(target_inquiry_dic[same_question])
            temp_set = target_inquiry_dic[same_question].difference(temp_samp_option_set)
            if len(target_inquiry_dic[same_question]) == 1:
                for item in temp_set:
                    if item != '正常':
                        normal_diff_list.append((same_question + '##' + item, same_question + '##' + '-'))
            else:
                for item in temp_set:
                    if item == '正常':
                        tres_remain_list.append(same_question + '##' + item)
                    else:
                        normal_diff_list.append((same_question + '##' + item, same_question + '##' + '-'))
            temp_set = source_inquiry_dic[same_question].difference(temp_samp_option_set)
            if len(source_inquiry_dic[same_question]) == 1:
                for item in temp_set:
                    if item != '正常':
                        normal_diff_list.append((same_question + '##' + '-', same_question + '##' + item))
            else:
                for item in temp_set:
                    if item == '正常':
                        sres_remain_list.append(same_question + '##' + item)
                    else:
                        normal_diff_list.append((same_question + '##' + '-', same_question + '##' + item))
            if '正常' in temp_samp_option_set:
                normal_same_list.append((same_question + '##-', same_question + '##-'))
                temp_samp_option_set.remove('正常')
            for tso in temp_samp_option_set:
                abnormal_same_list.append((same_question + '##' + tso, same_question + '##' + tso))
        res_dic = {'abnormal_same': abnormal_same_list, 'normal_diff': normal_diff_list,
                   'normal_same': normal_same_list,
                   'target_remain': tres_remain_list,
                   'source_remain': sres_remain_list}
        return res_dic

    # 解析自定义格式的问诊单
    def parse_custom_inquiry(self, inquiry_str):
        if not isinstance(inquiry_str, str):
            raise TypeError('Invalid data type: inquiry_str.')
        inquiry_str = inquiry_str.strip()
        if not inquiry_str or inquiry_str == 'NULL' or self.inquiry_map is None:
            return {}, []
        res_dic = {}
        remain_list = []
        que_ans = inquiry_str.split('@@')
        for qa in que_ans:
            items = qa.split('##')
            if len(items) != 2:
                remain_list.append(qa)
                continue
            question = items[0].replace('（可多选）', '')
            question = question.replace('(可多选)', '')
            options = items[1].split('$$')
            for option in options:
                temp_qa = question + '##' + option
                if temp_qa in self.inquiry_map:
                    temp_standard_qa = self.inquiry_map[temp_qa]
                    if temp_standard_qa[0] in res_dic:
                        res_dic[temp_standard_qa[0]].add(temp_standard_qa[1])
                    else:
                        res_dic[temp_standard_qa[0]] = {temp_standard_qa[1]}
                else:
                    remain_list.append(items[0] + '##' + option)
        return res_dic, remain_list

    # 读取标准问诊单映射字典
    @staticmethod
    def _read_inquiry_mapping(mapping_file):
        mapping_dic = {}
        with codecs.open(mapping_file, 'r', 'utf-8') as f:
            for line in f.readlines():
                line = line.rstrip()
                if not line:
                    continue
                if line.startswith('\t'):
                    items = line.strip().split('\t')
                    if len(items) == 2:
                        mapping_dic[question + '##' + items[0]] = items[1].split('##')
                else:
                    question = line.strip()
        return mapping_dic

    # 更改显示格式
    @staticmethod
    def _get_diff_terms(data_dic, same_set):
        res_list = []
        for k, v in data_dic.items():
            if k in same_set:
                continue
            for option in v:
                res_list.append(k + '##' + option)
        return res_list

    # 获取两个主诉之间的Jacard相似度
    def self_statement_distance(self, report_01, report_02):
        # 用主诉去匹配疾病和症状词表
        # report_01 = self.report_to_word(report_01)
        # report_02 = self.report_to_word(report_02)
        # 计算距离
        # count = 0
        # for i in report_01:
        #     if i in report_02:
        #         count += 1
        # report_01.extend(report_02)
        # temp_set = set(report_01)
        # if len(temp_set) == 0:
        #     distance = 0
        # else:
        #     distance = count / len(temp_set)
        # return distance
        set1 = set(report_01)
        set2 = set(report_02)
        len_union = len(set1.union(set2))
        if len_union == 0:
            return 0
        return len(set1.intersection(set2)) / len_union

    # 将主诉与疾病和症状词表进行匹配，得到匹配之后的词语列表
    def report_to_word(self, report):
        report_list = []
        for word in self.word_list:
            if word in report:
                report_list.append(word)
                report.replace(word, "  ")
        return report_list

    # 主诉匹配结果展示
    @staticmethod
    def report_display(report_target, report_source):
        report_dict = {}
        set1 = set(report_target)
        set2 = set(report_source)
        report_dict["report_same"] = set1.intersection(set2)
        report_dict["target_remain"] = set1 - set1.intersection(set2)
        report_dict["source_remain"] = set2 - set1.intersection(set2)
        return report_dict

    # 返回疾病和症状词表
    def _word_list(self):
        word_list = []
        disease_name = []
        symptom_name = []
        # 疾病列表
        workbook = xlrd.open_workbook(self.disease_name)
        worksheet = workbook.sheet_by_index(0)
        n_rows = worksheet.nrows
        for i in range(n_rows):  # 循环打印每一行
            row = worksheet.row_values(i)  # 以列表形式读出，列表中的每一项是str类型
            disease_name.append(row[1])
        # 症状列表
        symptom_name = self._symptom_list()
        # 总词表
        word_list.extend(disease_name)
        word_list.extend(symptom_name)
        return word_list

    # 症状/体征词表
    def _symptom_list(self):
        symptom_list = []
        # list_files = [codecs.open(self.symptom_name + "\\" + "chapter_0{0}.txt".format(i), 'rb') for i in range(1, 7)]
        for i in range(1, 7):
            with open(self.symptom_name + "/" + "chapter_0{0}.txt".format(i), 'r', encoding="utf-8") as fr:
                for line in fr.readlines():
                    if len(line.split('.')) < 2:
                        pass
                    else:
                        symptom_list.append(line.split('.')[-1].strip())
        return symptom_list

    # 性别，年龄，身高，体重的距离
    def basic_info_distance(self, basic_info_01, basic_info_02, weight_aas=2, weight_nad=-1):
        basic_similarity = 0
        basic_vec01_new = self._basic_info_vector(basic_info_01)  # ['NULL', 0, 3]
        basic_vec02_new = self._basic_info_vector(basic_info_02)  # [1.0, 2, 3]
        for i in range(len(basic_vec01_new)):
            if basic_vec01_new[i] == basic_vec02_new[i] and basic_vec01_new[i] != "NULL":
                basic_similarity += weight_aas
            elif basic_vec01_new[i] != basic_vec02_new[i] and basic_vec01_new[i] != "NULL" and basic_vec02_new[
                i] != "NULL":
                basic_similarity += weight_nad
        return basic_similarity

    # 将基本信息转化成数字表示的向量
    # [性别，年龄，BMI][0,1,"NULL"][0,1,2,3,4,"NULL"][0,1,2,3,"NULL"]
    # 儿童（～11）、少年（12～18）、青年（19～35）、中年（36～59）、老年（60～）
    @staticmethod
    def _basic_info_vector(basic_info):
        basic_vector = []
        temple_vector = []
        for index in range(len(basic_info)):
            if basic_info[index] == "NULL":
                basic_info_temp = "NULL"
            else:
                basic_info_temp = float(basic_info[index])
            temple_vector.append(basic_info_temp)
        basic_vector.append(temple_vector[0])  # 添加性别项
        age = "NULL"
        if temple_vector[1] != "NULL":
            if temple_vector[1] <= 11:
                age = 0
            elif 11 < temple_vector[1] <= 18:
                age = 1
            elif 18 < temple_vector[1] <= 35:
                age = 2
            elif 35 < temple_vector[1] <= 59:
                age = 3
            elif temple_vector[1] > 59:
                age = 4
        basic_vector.append(age)  # 添加年龄项
        bmi_result = "NULL"
        if temple_vector[2] != "NULL" and temple_vector[3] != "NULL":
            if (temple_vector[2] / 100) ** 2 == 0:
                pass
            else:
                bmi = temple_vector[3] / (temple_vector[2] / 100) ** 2
                if bmi <= 18.4:
                    bmi_result = 0
                elif bmi <= 23.9:
                    bmi_result = 1
                elif bmi <= 27.9:
                    bmi_result = 2
                else:
                    bmi_result = 3
        basic_vector.append(bmi_result)  # 添加BMI项
        return basic_vector

    # 整体相似度结果[基本信息，问诊单，主诉，舌照]
    # patient_info是一个字符串列表[]
    # patient_id, sex, age, height,	weight,	inquiry_sheet, tongue_picture,
    # tongue_feature, tongue_description, report, diagnosis, herbs
    def similarity_result(self, patient_info_01, patient_info_02, weight_list=(1, 1, 100, -1)):
        basic_similarity = self.basic_info_distance(patient_info_01[0], patient_info_02[0])
        # basic_similarity = 0
        statement_similarity = self.self_statement_distance(patient_info_01[1], patient_info_02[1])
        # statement_similarity = 0
        inquiry_similarity = self.inquiry_distance(patient_info_01[2], patient_info_02[2])
        # inquiry_similarity = 0
        tongue_distance = self.tongue_distance(patient_info_01[3], patient_info_02[3])
        # tongue_distance = 0
        similarity = basic_similarity * weight_list[0] + inquiry_similarity * weight_list[1] \
                     + statement_similarity * weight_list[2] + tongue_distance * weight_list[3]
        return similarity


if __name__ == '__main__':
    vec1 = '[6.33, 0.67, 2, 2.67, 3, 0, 0, 1, 0.33, 1.33, 2.67, 3, 5, 0, 1.33]'
    vec2 = '[3.33, 0.33, 0, 2.67, 3, 1.33, 0, 0.67, 0.33, 0.67, 1.67, 3, 3.67, 0, 0.67]'
    basec_01 = ["NULL", "0", "55", "20"]
    basec_02 = ["1", "20", "155", "120"]
    basec_03 = ["0", "69", "NULL", "NULL"]
    # tongue_similarity = SimilarityCalculation()
    # 整体测试
    # with codecs.open(r"D:\project_similarity\data\data_file_api\data_all_deduplicate_with_prescript_new.txt",
    #                  "r", encoding='utf-8') as fr:
    #     count = 0
    #     for line in fr.readlines()[1:]:
    #         count += 1
    #         value_list = line.split("\t")
    #         for index in range(len(value_list)):
    #             value_list[index] = value_list[index].strip()
    #         if count == 1:
    #             patient_01 = value_list
    #         if count == 3:
    #             patient_02 = value_list
    #             break
    # print(patient_01)
    # print(patient_02)
    # print(tongue_similarity.similarity_result(patient_01, patient_02))
    # 基本信息测试
    # print(tongue_similarity.basic_info_distance(basec_01, basec_02))
    # print(tongue_similarity.basic_info_distance(basec_02, basec_03))
    # ['NULL', 0, 3][1.0, 2, 3] 1 [1.0, 2, 3][0.0, 4, 'NULL'] -2
    # 舌照测试
    # print(tongue_similarity.tongue_vec_feature(vec1))
    # print(tongue_similarity.tongue_vec_feature(vec2))
    # print(tongue_similarity.tongue_contrast(vec1, vec2))
    # {'same_feature': [['红点:有点', '红点:有点'], ['芒刺:有点', '芒刺:有点']]……}
    # print(tongue_similarity.tongue_distance(vec1,vec2))
    # 14.98
    # print(tongue_similarity.word_list)
    # # 主诉测试
    # self_statement = "最近晚上难以入睡，多梦，中途醒很多次，身体燥热会掀被子。" \
    #                  "所以早上起来的时候有点鼻塞，月经不调，腰痛" \
    #                  "情绪烦躁"
    # self_statement_2 = "因为出现扭脖子，张嘴症状半年去上海儿童医学中心首诊，诊" \
    #                    "断出轻度抽动症，多动性障碍。开了择思达和静灵口服液，服药2" \
    #                    "1天后发现吃药以后脾气变的很暴躁，行为反常，嗜睡，胃口差，就自行" \
    #                    "停药一月。后来继续服药2个多月，病情没改善，反而越来越严重。未" \
    #                    "就诊前有注意力不集中，做事拖拉，不整理东西，记性差，单词和" \
    #                    "汉字记不住，好做小动作现象，但程度比现在轻，成绩中" \
    #                    "等，服药以后，以上现象症状加重，成绩更差，而且脾气非" \
    #                    "常差，难沟通，易激怒，拖拉症状更严重。服药后张嘴和" \
    #                    "扭脖子症状有所减轻，偶尔会扭，请问中医治疗这两种病有效果吗？"
    # print(tongue_similarity.report_to_word(self_statement))
    # print(tongue_similarity.report_to_word(self_statement_2))
    # print(tongue_similarity.self_statement_distance(self_statement,self_statement_2))
    # 问诊单测试
    # def custom_test():
    #     inquiry_str1 = ('您身体的冷热感受如何？（可多选）##怕冷@@您的出汗情况如何？（可多选）##出汗正常@@'
    #                     '您的身体是否有疼痛？（可多选）##胸痛$$腰痛$$肩颈痛$$肌肉痛$$关节痛$$周身疼痛@@'
    #                     '您的身体除疼痛外，是否还有以下不适？（可多选）##头晕$$胃胀$$腹胀$$手脚常年冰凉@@'
    #                     '您的眼部是否有不适？（可多选）##眼睛胀痛$$眼角红赤$$眼部充血$$眼部瘙痒@@'
    #                     '您的耳部是否有不适？（可多选）##无耳部不适@@您的口部是否有不适？（可多选）##口臭$$嘴唇干裂'
    #                     '@@您的咽喉是否有不适？（可多选）##无咽喉不适@@您是否有咳嗽喘痰等症状？（可多选）##'
    #                     '无咳嗽喘痰@@您的睡眠情况如何？（可多选）##多梦$$易醒@@您的口渴及饮水情况如何？（可多选）'
    #                     '##很少口渴$$喜喝凉水@@您的大便频率如何？##大便每天或隔天一次@@您的大便性状如何？（可多选）'
    #                     '##大便粘稠@@您的大便颜色如何？##大便呈黑色@@您的小便频率及尿量如何？（可多选）##'
    #                     '小便频率正常@@您的小便色质如何？（可多选）##小便黄@@您现在是否处于以下特殊阶段？##'
    #                     '非特殊阶段@@您的生育情况如何？##未生育@@您的流产情况如何？##未曾流产@@您的月经是否规律？'
    #                     '##月经提前@@您的月经经量是否正常？##经量正常@@您的月经经色、经质如何？（可多选）##'
    #                     '经血暗红$$经血中有血块@@您的白带情况是否正常？（可多选）##白带量多$$白带有腥臭味$$阴部瘙痒')
    #     inquiry_str2 = ('您身体的冷热感受如何？（可多选）##怕冷@@您的出汗情况如何？（可多选）##出汗正常@@'
    #                     '您的身体是否有疼痛？（可多选）##胁肋痛$$腰痛@@您的身体除疼痛外，是否还有以下不适？'
    #                     '（可多选）##手脚常年冰凉@@您的眼部是否有不适？（可多选）##迎风流泪$$眼睛分泌物过多@@'
    #                     '您的耳部是否有不适？（可多选）##耳鸣@@您的口部是否有不适？（可多选）##口苦@@'
    #                     '您的咽喉是否有不适？（可多选）##咽干@@您是否有咳嗽喘痰等症状？（可多选）##痰粘稠@@'
    #                     '您的睡眠情况如何？（可多选）##入睡难@@您的口渴及饮水情况如何？（可多选）##经常口渴@@'
    #                     '您的大便频率如何？##大便一天几次@@您的大便性状如何？（可多选）##大便干结@@'
    #                     '您的大便颜色如何？##大便呈黑色@@您的小便频率及尿量如何？（可多选）##夜尿多@@'
    #                     '您的小便色质如何？（可多选）##小便淡黄@@您是否有以下性功能障碍？（可多选）##晨勃消失@@'
    #                     '您是否曾患过以下疾病？（可多选）##未曾患过前列腺相关疾病')
    #     inquiry_str3 = 'NULL'
    #     score = tongue_similarity.inquiry_distance(inquiry_str1, inquiry_str2)
    #     print(score)
    #     ref_dic = tongue_similarity.inquiry_display(inquiry_str1, inquiry_str2)
    #     for k, v in ref_dic.items():
    #         print('{0}: {1}'.format(k, v))
    # custom_test()
